package com.gylang.springmini.support;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;

/**
 * @author gylang
 * data 2021/11/15
 */

public class RootBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition {

    private String parentName;
    private String beanClassName;
    private ClassLoader classLoader;
    private String scope;
    private Boolean lazyInit;
    private String[] dependsOn = {};
    private boolean autowireCandidate;
    private boolean primary;
    private String factoryBeanName;
    private String factoryMethodName;
    private final ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
    private final MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
    private String initMethodName;
    private String destroyMethodName;
    private int role;
    private BeanDefinition originatingBeanDefinition;


    public RootBeanDefinition() {
    }

    @Override
    public RootBeanDefinition clone() {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setParentName(this.parentName);
        rootBeanDefinition.originatingBeanDefinition = this;
        rootBeanDefinition.setBeanClassName(this.getBeanClassName());
        rootBeanDefinition.setScope(this.scope);
        rootBeanDefinition.setLazyInit(this.lazyInit);
        rootBeanDefinition.setDependsOn(this.dependsOn);
        rootBeanDefinition.setPrimary(this.primary);
        rootBeanDefinition.setFactoryBeanName(this.factoryBeanName);
        rootBeanDefinition.setFactoryMethodName(this.factoryMethodName);
        rootBeanDefinition.setInitMethodName(this.initMethodName);
        rootBeanDefinition.setDestroyMethodName(this.destroyMethodName);
        rootBeanDefinition.setRole(this.role);
        rootBeanDefinition.setAutowireCandidate(this.autowireCandidate);
        rootBeanDefinition.mutablePropertyValues.addPropertyValues(getPropertyValues());
        rootBeanDefinition.constructorArgumentValues.addArgumentValues(getConstructorArgumentValues());
        return rootBeanDefinition;
    }


    @Override
    public void setParentName(String parentName) {
        this.parentName = parentName;
    }

    @Override
    public String getParentName() {
        return this.parentName;
    }

    @Override
    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }

    @Override
    public void setBeanClassClassLoad(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public ClassLoader getBeanClassClassLoad() {
        return this.classLoader;
    }

    @Override
    public String getBeanClassName() {
        return this.beanClassName;
    }

    @Override
    public void setScope(String scope) {
        this.scope = scope;
    }

    @Override
    public String getScope() {
        return this.scope;
    }

    @Override
    public void setLazyInit(Boolean lazyInit) {
        this.lazyInit = lazyInit;
    }

    @Override
    public boolean isLazyInit() {
        return Boolean.TRUE.equals(lazyInit);
    }

    @Override
    public Boolean getLazyInit() {
        return lazyInit;
    }

    @Override
    public void setDependsOn(String... dependsOn) {
        this.dependsOn = dependsOn;
    }

    @Override
    public String[] getDependsOn() {
        return this.dependsOn;
    }

    @Override
    public void setAutowireCandidate(Boolean autowireCandidate) {
        this.autowireCandidate = autowireCandidate;
    }

    @Override
    public boolean isAutowireCandidate() {
        return this.autowireCandidate;
    }

    @Override
    public void setPrimary(Boolean primary) {
        this.primary = primary;
    }

    @Override
    public boolean isPrimary() {
        return this.primary;
    }

    @Override
    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }

    @Override
    public String getFactoryBeanName() {
        return this.factoryBeanName;
    }

    @Override
    public void setFactoryMethodName(String factoryMethodName) {
        this.factoryMethodName = factoryMethodName;
    }

    @Override
    public String getFactoryMethodName() {
        return this.factoryMethodName;
    }

    @Override
    public ConstructorArgumentValues getConstructorArgumentValues() {
        return this.constructorArgumentValues;
    }

    @Override
    public Object[] getConstructorArgumentValuesArr() {
        return constructorArgumentValues.getGenericArgumentValues().stream()
                .map(ConstructorArgumentValues.ValueHolder::getValue)
                .toArray();
    }

    @Override
    public MutablePropertyValues getPropertyValues() {
        return this.mutablePropertyValues;
    }

    @Override
    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    @Override
    public String getInitMethodName() {
        return this.initMethodName;
    }

    @Override
    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }

    @Override
    public String getDestroyMethodName() {
        return this.destroyMethodName;
    }

    @Override
    public void setRole(int role) {
        this.role = role;
    }

    @Override
    public int getRole() {
        return this.role;
    }

    @Override
    public boolean isSingleton() {
        return SCOPE_SINGLETON.equals(scope);
    }

    @Override
    public boolean isPrototype() {
        return SCOPE_PROTOTYPE.equals(scope);
    }

    @Override
    public boolean isAbstract() {
        return ClassUtil.isAbstract(ClassLoaderUtil.loadClass(beanClassName, classLoader, false));
    }

    @Override
    public BeanDefinition getOriginatingBeanDefinition() {
        return this.originatingBeanDefinition;
    }

    @Override
    public void mergeSuperBeanDefinition(BeanDefinition beanDefinition) {
        // 合并属性 生命周期方法等
        if (StrUtil.isEmpty(this.scope)) {
            this.scope = beanDefinition.getScope();
        }
        if (null == this.lazyInit) {
            this.lazyInit = beanDefinition.getLazyInit();
        }
        this.dependsOn = ArrayUtil.addAll(this.dependsOn, beanDefinition.getDependsOn());

        this.primary = this.primary & beanDefinition.isPrimary();

        if (StrUtil.isEmpty(factoryBeanName)) {
            this.factoryBeanName = beanDefinition.getFactoryBeanName();
        }
        if (StrUtil.isEmpty(factoryMethodName)) {
            this.factoryMethodName = beanDefinition.getFactoryMethodName();
        }
        if (StrUtil.isEmpty(initMethodName)) {
            this.initMethodName = beanDefinition.getInitMethodName();
        }
        if (StrUtil.isEmpty(destroyMethodName)) {
            this.destroyMethodName = beanDefinition.getDestroyMethodName();
        }
        beanDefinition.getPropertyValues().stream()
                .filter(p -> !mutablePropertyValues.contains(p.getName()))
                .forEach(mutablePropertyValues::addPropertyValue);
    }


}
